/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controle.socket;

import controle.gui.frames.internos.ControleSincronizacao;
import controle.interfaces.Constantes;
import controle.utils.Datas;
import controle.utils.Horas;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
import modelo.*;
import persistencia.dao.AbstractDAO;
import persistencia.dao.TransacaoDAO;

/**
 * Classe de Controle da conexão socket do servidor (Thread)
 *
 * @author Maycon Fernando Silva Brito
 * @email mayconfsbrito@gmail.com
 */
public class ServidorSocket implements Runnable {

    /**
     * Variáveis da classe
     */
    public static boolean executando = true;
    private ServerSocket server = null;
    private Socket socket = null;
    private ControleSincronizacao controle = null;
    private String msg = null;
    private DataInputStream in = null;
    private DataOutputStream out = null;
    private OutputStream ous = null;

    //Construtor da classe
    public ServidorSocket(ControleSincronizacao controle) {
        this.controle = controle;
    }

    /**
     * Executa este método como uma thread
     */
    @Override
    public void run() {
        abreConexao();
    }

    /**
     * Disponibiliza o servidor para realizar a conexão com o dispositivo móvel
     *
     * @return
     */
    public void abreConexao() {

        //Captura o número da porta salvo em configurações no bd
        Configuracao conf = (Configuracao) AbstractDAO.consultar("Configuracao", "id=1").get(0);
        int PORTA = conf.getPorta();

        try {

            //Procura novas conexões com clientes
            while (executando) {

                controle.guiAtiva(true);

                //Cria socket para conexão
                server = new ServerSocket(PORTA);
                InetAddress addr = InetAddress.getLocalHost();
                controle.addMessage("------ SERVIDOR CONECTADO " + addr.getHostAddress() + " PORTA " + PORTA + " ------ (" + Horas.getHoraAgora().toString() + ")");
                controle.addMessage("Aguardando Conexões.");
                socket = server.accept();
                controle.addMessage("Conectado ao host " + socket.getInetAddress().getHostName());

                //Inicializa Streams
                in = new DataInputStream(socket.getInputStream());
                ous = socket.getOutputStream();
                out = new DataOutputStream(socket.getOutputStream());
                out.flush();

                //Mensagens
                msg = in.readUTF();
                controle.addMessage("Recebeu Mensagem: " + msg);

                //Caso a mensagem seja para sincronizar apenas usuários
                if (msg.equals(Constantes.SINC_USUARIOS)) {
                    sincronizaUsuarios(out, in);

                    //Caso a mensagem seja para sincronização completa
                } else if (msg.equals(Constantes.SINC_COMPLETO)) {
                    sincronizacaoCompleta(out, in, ous);

                } else if (msg.equals(Constantes.SINC_BOL)) {
                    sincronizaBoletins(out, in, msg, false);

                } else if (msg.equals(Constantes.DISCONNECTED)) {
                    executando = false;
                }

            }

        } catch (SocketException e) {
            try {

                if (socket != null && socket.isConnected()) {
                    socket.close();
                }

            } catch (IOException er) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            controle.addMessage("Erro de I/O de mensagens durante a conexão com o dispositivo móvel.");
            e.printStackTrace();

        } catch (Exception e) {
            controle.addMessage("Erro ao estabelecer conexao com o dispositivo móvel.");
            e.printStackTrace();

        }

    }

    /**
     * Sincroniza os usuarios do bd servidor com a conexao
     */
    public void sincronizaUsuarios(DataOutputStream out, DataInputStream in) {

        try {
            controle.addMessage("Sincronizando usuários!");
            List<Agente> list = AbstractDAO.consultar("Agente", "codigo>0");

            //Envia a confirmacao para o cliente apagar todos os usuarios
            out.writeUTF(Constantes.OK);

            //Verifica se o cliente está pronto para receber os usuários
            if (verificaRespostaOperacao(in)) {
                controle.addMessage("Usuários enviados: " + list.size());

                //Envia a quantidade de usuários a serem enviados para o cliente
                out.writeInt(list.size());

                //Envia todos os usuarios ativos e validos para o android, um de cada vez
                while (list.size() > 0) {

                    //Envia uma mensagem informando que enviará mais um objeto
                    out.writeUTF(Constantes.OK);

                    //Envia o objeto atual (Agente)
                    Agente age = list.get(0);
                    //id
                    out.writeInt(age.getIdAgente());
                    //codigo
                    out.writeInt(age.getCodigo());
                    //nome
                    out.writeUTF(age.getNome());
                    //ativo
                    out.writeBoolean(age.isAtivo());
                    //login
                    out.writeUTF(age.getLogin());
                    //senha
                    out.writeUTF(age.getSenha());
                    //datacadastro
                    out.writeUTF(Datas.getText(age.getDataCadastro(), "yyyy-MM-dd"));
                    //horacadastro
                    out.writeUTF(age.getHoraCadastro().toString());
                    //numeroAmostra
                    out.writeInt(age.getNumeroAmostra());
                    //anoAmostra
                    out.writeInt(age.getAnoAmostra());

                    //Remove o objeto da lista
                    list.remove(0);
                }

                try {
                    //Aguarda a resposta do cliente enviando a conclusão da sincronização
                    verificaRespostaOperacao(in);
                } catch (EOFException ex) {
                    verificaRespostaOperacao(in);
                }

            }

            TransacaoDAO.cadastrar("Sincronizou agentes com o dispositivo móvel.");

        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    /**
     * Sincroniza apenas os boletins
     */
    public void sincronizaBoletins(DataOutputStream out, DataInputStream in, String msg, boolean isSincCompleta) throws IOException {

        // Recebe a requisição de sincronização apenas de boletins
        if (msg.equals(Constantes.SINC_BOL)) {
            controle.addMessage("Sincronizando Boletins...");

            //Envia uma mensagem de OK para o cliente
            out.writeUTF(Constantes.OK);

            //Sincroniza numero e ano da amostra de todos os usuários da apk
            sincronizaAmostraUsuariosAPK(in, out);

            //Recebe o código do município
            String cod = in.readUTF();

            //Verifica se o código do município existe
            List<Municipio> list = AbstractDAO.consultar("Municipio", "codigo=" + cod);
            //Caso o código do município exista
            if (list.size() > 0) {
                //Responde que o município existe
                out.writeUTF(Constantes.OK);
                out.flush();
                controle.addMessage("Código de município encontrado!");

                //Envia o município
                enviaMunicipio(out, list.get(0));

                out.writeUTF(Constantes.OK);

                //Recebe a quantidade de boletins de tratamento a serem sincronizados
                Integer qtd = in.readInt();
                controle.addMessage("Boletins de tratamento sendo sincronizados: " + qtd);
                //Sincroniza os boletins de tratamento
                for (int index = 0; index < qtd; index++) {
                    recebeBoletimtratamento(in);
                }
                controle.addMessage("Boletins de tratamento sincronizados.");

                //Recebe a quantidade de boletins de pesquisa a serem sincronizados
                qtd = in.readInt();
                controle.addMessage("Boletins de pesquisa sendo sincronizados: " + qtd);
                //Sincroniza os boletins de tratamento
                for (int index = 0; index < qtd; index++) {
                    recebeBoletimpesquisa(in);
                }
                controle.addMessage("Boletins de pesquisa sincronizados.");

                if (!isSincCompleta) {
                    //Log de Usuários
                    TransacaoDAO.cadastrar("Sincronizou boletins do município código=" + list.get(0).getCodigo() + " com o dispositivo móvel.");
                }
            } else {
                //Responde que o município não existe
                out.writeUTF(Constantes.CANCEL);
                controle.addMessage("Código de município inexistente!");

            }

            if (!isSincCompleta) {
                //Aguarda a resposta do cliente enviando a conclusão da sincronização
                verificaRespostaOperacao(in);
            }
        }
    }

    /**
     * Sincroniza numero e ano da amostra de todos os usuários da apk
     *
     * @throws IOException
     */
    public boolean sincronizaAmostraUsuariosAPK(DataInputStream in, DataOutputStream out) throws IOException {


        msg = in.readUTF();
        
        controle.addMessage("Sincronizando amostras.");

        //sincroniza a amostra de cada agente da apk
        while (msg.equals(Constantes.OK)) {

            //recebe o id do usuário
            Integer apkIdAgente = in.readInt();

            //recebe o número da amostra do usuário
            Integer apkNumAmostra = in.readInt();

            //recebe o ano da amostra do usuário
            Integer apkAnoAmostra = in.readInt();

            //Busca no bd o agente
            Agente age = (Agente) AbstractDAO.consultar("Agente", "idAgente=" + apkIdAgente).get(0);

            System.out.println(apkNumAmostra + " " + apkAnoAmostra);
            
            //Compara o ano da amostra
            Integer maiorAmostra = 0;
            Integer maiorAno = 0;
            //caso os dois anos sejam iguais
            if (apkAnoAmostra.equals(age.getAnoAmostra())) {

                //Captura o mesmo ano
                maiorAno = apkAnoAmostra;
                
                //Captura o maior número de amostra
                if (apkNumAmostra >= age.getNumeroAmostra()) {
                    maiorAmostra = apkNumAmostra;
                } else {
                    maiorAmostra = age.getNumeroAmostra();
                }
                
                //caso o ano da apk seja maior
            } else if(apkAnoAmostra > age.getAnoAmostra()){
                
                //Captura o ano da amostra da apk
                maiorAno = apkAnoAmostra;
                
                //Captura o número da amostra da apk
                maiorAmostra = apkNumAmostra;
                
                
                //caso o ano do servidor seja menor
            } else {
                
                //Captura o ano da amostra do servidor
                maiorAno = age.getAnoAmostra();
                
                //Captura o número da amostra do servidor
                maiorAmostra = age.getNumeroAmostra();
            }

            //envia o novo número de amostra
            out.writeInt(maiorAmostra);

            //envia o novo ano de amostra 
            out.writeInt(maiorAno);

            System.out.println(maiorAmostra + " " + maiorAno);
            
            //Caso a amostra ou o ano tenham sido alterados
            if (!age.getNumeroAmostra().equals(maiorAmostra) || !age.getAnoAmostra().equals(maiorAno)) {
                //Altera no bd o agente com a nova amostra e ano
                age.setNumeroAmostra(maiorAmostra);
                age.setAnoAmostra(maiorAno);
                AbstractDAO.alterar(age);
            }

            //Recebe a nova mensagem, OK ou CANCEL
            msg = in.readUTF();
            
        }
        
        controle.addMessage("Amostras sincronizadas.");

        return true;
    }

    /**
     * Realiza a sincronização completa
     */
    public void sincronizacaoCompleta(DataOutputStream out, DataInputStream in, OutputStream ous) {

        long inicio = System.currentTimeMillis();
        try {
            controle.addMessage("Sincronização completa!");

            //Envia a confirmacao para o cliente de que a sincronização será feita
            out.writeUTF(Constantes.OK);

            //Recebe o código do município
            String cod = in.readUTF();

            //Verifica se o código do município existe
            List<Municipio> list = AbstractDAO.consultar("Municipio", "codigo=" + cod);
            //Caso o código do município exista
            if (list.size() > 0) {
                //Responde que o município existe
                out.writeUTF(Constantes.OK);
                out.flush();
                controle.addMessage("Código de município encontrado!");

                //Envia o município
                Municipio mun = list.get(0);
                enviaMunicipio(out, list.get(0));

                //Envia a quantidade de localidades a serem enviadas
                List<Localidade> listLoc = AbstractDAO.consultar("Localidade", "idMunicipio=" + mun.getIdMunicipio());
                out.writeInt(listLoc.size());
                out.flush();

                //Verifica a qtd de localidades a serem sincronizadas
                controle.addMessage("Localidades sendo sincronizadas: " + listLoc.size());
                if (listLoc.size() > 0) {
                    //Envia todas as localidades do município
                    while (listLoc.size() > 0) {

                        //Envia uma localidade
                        enviaLocalidade(out, listLoc.get(0));

                        listLoc.remove(0);
                    }
                }
                controle.addMessage("Localidades sincronizadas.");

                //Envia a quantidade de logradouros a serem enviados
                List<Logradouro> listLog = AbstractDAO.consultar("Logradouro", "idMunicipio=" + mun.getIdMunicipio());
                out.writeInt(listLog.size());
                out.flush();

                //Verifica a qtd de logradouros a serem sincronizados
                controle.addMessage("Logradouros sendo sincronizados: " + listLog.size());
                if (listLog.size() > 0) {
                    //Envia todos os logradouros do município
                    while (listLog.size() > 0) {
                        //Envia um logradouro
                        enviaLogradouro(out, listLog.get(0));
                        listLog.remove(0);
                    }
                }
                controle.addMessage("Logradouros sincronizados.");

                //Envia a quantidade de Quadras a serem enviadas
                List<Quadra> listQua = AbstractDAO.consultarSemFecharSessao(null, "from Quadra q left join fetch q.localidade as l where l.municipio.idMunicipio=" + mun.getIdMunicipio());
                out.writeInt(listQua.size());
                out.flush();

                //Envia as Quadras do município
                controle.addMessage("Quadras sendo sincronizadas: " + listQua.size());
                if (listQua.size() > 0) {
                    //Envia todas as quadras do município
                    while (listQua.size() > 0) {
                        //Envia uma quadra
                        enviaQuadra(out, listQua.get(0));
                        listQua.remove(0);
                    }
                }
                AbstractDAO.fechaSessao();
                controle.addMessage("Quadras sincronizadas.");

                //Envia a quantidade de imóveis a serem enviados
                List<Imovel> listImo = AbstractDAO.consultar("from Imovel i left join fetch i.logradouro as l where l.municipio.idMunicipio=" + mun.getIdMunicipio());
                out.writeInt(listImo.size());
                out.flush();

                //Envia os Imóveis do município
                controle.addMessage("Imóveis sendo sincronizados: " + listImo.size());
                if (listImo.size() > 0) {
                    //Envia todos os imóveis do município
                    while (listImo.size() > 0) {
                        //Envia o imóvel
                        enviaImovel(out, listImo.get(0));
                        listImo.remove(0);
                    }
                }
                controle.addMessage("Imóveis sincronizados.");

                //Aguarda a resposta do cliente para sincronizar os boletins
                msg = in.readUTF();
                sincronizaBoletins(out, in, msg, true);

                //Sincroniza Croquis
                sincronizaCroquis(in, out, ous);

                //Log de Usuários
                TransacaoDAO.cadastrar("Sincronizou o município código=" + mun.getCodigo() + " com o dispositivo móvel.");

                //Caso o código do município não exista
            } else {
                //Responde que o município não existe
                out.writeUTF(Constantes.CANCEL);
                controle.addMessage("Código de município inexistente!");

            }

            try {
                //Aguarda a resposta do cliente enviando a conclusão da sincronização
                verificaRespostaOperacao(in);
            } catch (EOFException ex) {
                desconectar();
                controle.addMessage("Erro durante a sincronização! Veja o log para maiores detalhes.");
                ex.printStackTrace();
            }

        } catch (IOException ex) {
            desconectar();
            controle.addMessage("Erro durante a sincronização! Veja o log para maiores detalhes.");
            ex.printStackTrace();
        }

        System.out.println("Tempo total de sincronização: " + (System.currentTimeMillis() - inicio));

    }

    /**
     * Encerra a conexão atual
     */
    public void desconectar() {

        try {
            if (in != null) {
                in.close();
            }

            if (out != null) {
                out.close();
            }

            if (socket != null) {
                socket.close();
            }

            if (server != null) {
                server.close();
            }

            controle.addMessage("Conexão Finalizada.\n");
            executando = true;

        } catch (SocketException e) {
            try {

                if (socket != null && socket.isConnected()) {
                    socket.close();
                }

            } catch (IOException er) {
                e.printStackTrace();
            }
        } catch (Exception e) {

            controle.addMessage("Erro ao estabelecer conexao com o dispositivo móvel.");
            e.printStackTrace();

        }
    }

    /**
     * Verifica a resposta do cliente e continua ou cancela a conexão dependendo
     * da operação
     *
     * @return
     * @true se a operação poderá continuar,
     * @false se a operação deverá ser encerrada
     */
    public boolean verificaRespostaOperacao(DataInputStream input) throws IOException {

        msg = input.readUTF();
        controle.addMessage("Mensagem Recebida:" + msg);

        if (msg.equals(Constantes.OK)) {
            return true;

        } else {
            if (msg.equals(Constantes.CONCLUIDO)) {
                controle.addMessage("Sincronização concluída com sucesso!");

            } else if (msg.equals(Constantes.CANCEL)) {
                controle.addMessage("Sincronização cancelada!");
            }
            desconectar();
            controle.guiAtiva(false);
            return false;

        }
    }

    public boolean sincronizaCroquis(DataInputStream in, DataOutputStream out, OutputStream ous) throws IOException {

        //Aguarda um OK
        msg = in.readUTF();
        int qtd = 0;

        //Verifica a mensagem recebida
        if (msg.equals(Constantes.OK)) {
            controle.addMessage("Sincronizando croquis (Aguarde, essa etapa pode demorar)...");

            //Aguarda um pedido de sincronização
            msg = in.readUTF();

            //Enquanto o cliente estiver sincronizando croquis
            while (msg.equals(Constantes.SINC_CROQUI)) {

                //Caso a mensagem seja para sincronizar croqui
                if (msg.equals(Constantes.SINC_CROQUI)) {

                    //Recebe o idLocalidade
                    int idLocalidade = in.readInt();

                    //Abre o arquivo para envio
                    File file = new File("croquis/" + idLocalidade + ".png");
                    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                    long tamanho = file.length();
                    System.out.println("Tamanho do Arquivo:" + tamanho + " idLocalidade=" + idLocalidade);

                    //Envia o tamanho do arquivo
                    out.writeLong(tamanho);
                    out.flush();

                    //Cria um buffer
                    byte[] buffer = new byte[Constantes.BUFFER_SIZE];

                    //Envia dados
                    Integer leitura = null;
                    do {
                        leitura = bis.read(buffer, 0, buffer.length);
                        System.out.flush();
                        if (leitura != -1) {
                            ous.write(buffer, 0, leitura);
                        } else {
                            ous.write(leitura);
                        }
                        ous.flush();

                    } while (leitura != -1);

                    bis.close();
                    qtd++;
                }

                msg = in.readUTF();
            }
            controle.addMessage("Total de croquis sincronizados: " + qtd);
        }

        return true;
    }

    /**
     * Recebe um boletim de tratamento e suas visitas
     */
    public boolean recebeBoletimtratamento(DataInputStream in) throws IOException {

        Boletimtratamento bol = new Boletimtratamento();
        //idBoletimtratamento (NÃO É NECESSÁRIO)
        //idAgente
        Agente age = new Agente();
        age.setIdAgente(in.readInt());
        bol.setAgente(age);
        //idLocalidade
        Localidade loc = new Localidade();
        loc.setIdLocalidade(in.readInt());
        bol.setLocalidade(loc);
        //idMunicipio
        Municipio mun = new Municipio();
        mun.setIdMunicipio(in.readInt());
        bol.setMunicipio(mun);
        //coordenadorRegional
        bol.setCoordenadorRegional(in.readUTF());
        //grs
        bol.setGrs(in.readUTF());
        //categoria
        bol.setCategoria(in.readUTF());
        //turma
        bol.setTurma(in.readUTF());
        //data
        bol.setData(Datas.getDate(in.readUTF(), "yyyy-MM-dd"));
        //semanaEpid
        bol.setSemanaEpid(in.readUTF());
        //numeroAtividade
        bol.setNumeroAtividade(in.readUTF());
        //tipoAtividade
        bol.setTipoAtividade(in.readUTF());
        //sincronizadoEm
        bol.setSincronizadoEm(Datas.getDataHoje());
        //ativo
        bol.setAtivo(true);

        //Cadastra o boletim no bd
        if (AbstractDAO.cadastrar(bol)) {
            bol.setIdBoletimTratamento(AbstractDAO.max(Boletimtratamento.class, "idBoletimTratamento"));

            //Envia um OK
            out.writeUTF(Constantes.OK);

        } else {
            controle.addMessage("Não foi possível cadastrar o boletim.");

            //Envia um CANCEL
            out.writeUTF(Constantes.CANCEL);

            return false;
        }

        //Recebe a quantidade de visitas a serem sincronizadas
        int qtd = in.readInt();

        //Sincroniza cada visita, uma por uma
        for (int index = 0; index < qtd; index++) {
            Visitatratamento vis = new Visitatratamento();
            //idVisitatratamento (NÃO INSERE)
            //idBoletimtratamento 
            vis.setBoletimtratamento(bol);
            //idQuadra
            Quadra q = new Quadra();
            q.setIdQuadra(in.readInt());
            vis.setQuadra(q);
            //idLogradouro
            Logradouro l = new Logradouro();
            l.setIdLogradouro(in.readInt());
            vis.setLogradouro(l);
            //idImovel
            Imovel i = new Imovel();
            i.setIdImovel(in.readInt());
            vis.setImovel(i);
            //tipoUnidade
            vis.setTipoUnidade(in.readUTF());
            //inseticidaLarvicida
            vis.setInseticidaLarvicida(in.readInt());
            //inseticidaAdulticida
            vis.setInseticidaAdulticida(in.readInt());
            //pendenciaRec
            vis.setPendenciaRec(in.readBoolean());
            //pendenciaFech
            vis.setPendenciaFech(in.readBoolean());
            //pendenciaResg
            vis.setPendenciaResg(in.readBoolean());
            //despositosTratados
            vis.setDepositosTratados(in.readInt());
            //ultimaVisitaBoletim
            vis.setUltimaVisitaBoletim(in.readBoolean());
            //quarteiraoConcluido
            vis.setQuadraConcluida(in.readBoolean());
            try {
                //hora
                vis.setHora(Horas.getTime(in.readUTF()));
            } catch (ParseException ex) {
                ex.printStackTrace();
            }

            //Cadastra a visita
            AbstractDAO.cadastrar(vis);

            //Envia um OK confirmado o cadastro
            out.writeUTF(Constantes.OK);
        }

        //Envia um OK para o cliente apagar as suas visitas deste boletim
        out.writeUTF(Constantes.OK);

        return true;
    }

    /**
     * Recebe um boletim de pesquisa e suas visitas
     */
    public boolean recebeBoletimpesquisa(DataInputStream in) throws IOException {

        Boletimpesquisa bol = new Boletimpesquisa();
        //idBoletimpesquisa (NÃO É NECESSÁRIO)
        //idAgente
        Agente age = new Agente();
        age.setIdAgente(in.readInt());
        bol.setAgente(age);
        //idLocalidade
        Localidade loc = new Localidade();
        loc.setIdLocalidade(in.readInt());
        bol.setLocalidade(loc);
        //idMunicipio
        Municipio mun = new Municipio();
        mun.setIdMunicipio(in.readInt());
        bol.setMunicipio(mun);
        //coordenadorRegional
        bol.setCoordenadorRegional(in.readUTF());
        //grs
        bol.setGrs(in.readUTF());
        //categoria
        bol.setCategoria(in.readUTF());
        //turma
        bol.setTurma(in.readUTF());
        //data
        bol.setData(Datas.getDate(in.readUTF(), "yyyy-MM-dd"));
        //semanaEpid
        bol.setSemanaEpid(in.readUTF());
        //numeroAtividade
        bol.setNumeroAtividade(in.readUTF());
        //liAmostra
        bol.setLiAmostra(in.readInt());
        //tipoAtividade
        bol.setTipoAtividade(in.readUTF());
        //sincronizadoEm
        bol.setSincronizadoEm(Datas.getDataHoje());
        //ativo
        bol.setAtivo(true);

        //Cadastra o boletim no bd
        if (AbstractDAO.cadastrar(bol)) {
            bol.setIdBoletimPesquisa(AbstractDAO.max(Boletimpesquisa.class, "idBoletimPesquisa"));

            //Envia um OK
            out.writeUTF(Constantes.OK);

        } else {
            controle.addMessage("Não foi possível cadastrar o boletim.");

            //Envia um CANCEL
            out.writeUTF(Constantes.CANCEL);

            return false;
        }

        //Recebe a quantidade de visitas a serem sincronizadas
        int qtd = in.readInt();

        //Sincroniza cada visita, uma por uma
        for (int index = 0; index < qtd; index++) {
            Visitapesquisa vis = new Visitapesquisa();
            //idVisitapesquisa(NÃO INSERE)
            //idBoletimpesquisa 
            vis.setBoletimpesquisa(bol);
            //idQuadra
            Quadra q = new Quadra();
            q.setIdQuadra(in.readInt());
            vis.setQuadra(q);
            //idLogradouro
            Logradouro l = new Logradouro();
            l.setIdLogradouro(in.readInt());
            vis.setLogradouro(l);
            //idImovel
            Imovel i = new Imovel();
            i.setIdImovel(in.readInt());
            vis.setImovel(i);
            //tipoUnidade
            vis.setTipoUnidade(in.readUTF());
            //numeroTubitos
            vis.setNumeroTubitos(in.readInt());
            //numeroAmostra
            vis.setNumeroAmostra(in.readInt());
            //larvasAeg
            //larvasAlb
            //larvasOut
            //examinadoLaboratorio
            vis.setExaminadoLaboratorio(in.readBoolean());
            //despositosInspecionados
            vis.setDepositosInspecionados(in.readInt());
            //ultimaVisitaBoletim
            vis.setUltimaVisitaBoletim(in.readBoolean());
            //quadraConcluido
            vis.setQuadraConcluida(in.readBoolean());
            try {
                //hora
                vis.setHora(Horas.getTime(in.readUTF()));
            } catch (ParseException ex) {
                ex.printStackTrace();
            }

            //Cadastra a visita
            AbstractDAO.cadastrar(vis);

            //Envia um OK confirmado o cadastro
            out.writeUTF(Constantes.OK);
        }

        //Envia um OK para o cliente apagar as suas visitas deste boletim
        out.writeUTF(Constantes.OK);

        return true;
    }

    /**
     * Envia o município via Socket
     */
    public void enviaMunicipio(DataOutputStream out, Municipio mun) throws IOException {

        //id
        out.writeInt(mun.getIdMunicipio());
        //codigo
        out.writeUTF(mun.getCodigo());
        //nome
        out.writeUTF(mun.getNome());
        //estado
        out.writeUTF(mun.getEstado());

        controle.addMessage("Município '" + mun.getNome() + "' enviado.");

    }

    /**
     * Envia a Localidade via Socket
     */
    public void enviaLocalidade(DataOutputStream out, Localidade loc) throws IOException {

        //idLocalidade
        out.writeInt(loc.getIdLocalidade());
        //idMunicipio
        out.writeInt(loc.getMunicipio().getIdMunicipio());
        //codigo
        out.writeInt(loc.getCodigo());
        //nome
        out.writeUTF(loc.getNome());
        //isCroqui
        out.writeBoolean(loc.isCroqui());
        //dataCroqui
        String data = loc.getDataCroqui() == null ? "" : Datas.getText(loc.getDataCroqui(), "yyyy-MM-dd");
        out.writeUTF(data);
        //horaCroqui
        String hora = loc.getHoraCroqui() == null ? "" : loc.getHoraCroqui().toString();
        out.writeUTF(hora);

    }

    /**
     * Envia a Logradouro via Socket
     */
    public void enviaLogradouro(DataOutputStream out, Logradouro log) throws IOException {

        //idLogradouro
        out.writeInt(log.getIdLogradouro());
        //idMunicipio
        out.writeInt(log.getMunicipio().getIdMunicipio());
        //Nome
        out.writeUTF(log.getNome());
    }

    /**
     * Envia a quadra via Socket
     */
    public void enviaQuadra(DataOutputStream out, Quadra qua) throws IOException {

        //idQuadra
        out.writeInt(qua.getIdQuadra());
        //idLocalidade
        out.writeInt(qua.getLocalidade().getIdLocalidade());
        //codigo
        out.writeInt(qua.getCodigo());

        //Quantidade de quadras_lograouros
        out.writeInt(qua.getQuadralogradouros().size());

        //Envia cada Quadra_Logradouro desta quadra
        for (Iterator it = qua.getQuadralogradouros().iterator(); it.hasNext();) {
            Quadralogradouro ql = (Quadralogradouro) it.next();

            //idQuadraLogradouro
            out.writeInt(ql.getIdQuadraLogradouro());
            //idQuadra
            out.writeInt(ql.getQuadra().getIdQuadra());
            //idLogradouro
            out.writeInt(ql.getLogradouro().getIdLogradouro());
        }
    }

    /**
     * Envia o imóvel via Socket
     */
    public void enviaImovel(DataOutputStream out, Imovel imo) throws IOException {

        //idImovel
        out.writeInt(imo.getIdImovel());
        //idQuadra
        out.writeInt(imo.getQuadra().getIdQuadra());
        //idLogradouro
        out.writeInt(imo.getLogradouro().getIdLogradouro());
        //numero
        out.writeUTF(imo.getNumero());
        //complemento
        out.writeUTF(imo.getComplemento() != null ? imo.getComplemento() : "");

    }
}